package com.woniuxy.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.woniuxy.entity.AllRun;
import com.woniuxy.entity.OrderRun;
import com.woniuxy.entity.DTO.algorithm.OrderRunPredictionDTO;
import com.woniuxy.mapper.AllRunMapper;
import com.woniuxy.mapper.OrderRunMapper;
import com.woniuxy.service.IOrderRunPredictionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author 陈华庆
 * @Date 2025/7/22 20:38
 * @Description TODO
 * 跑腿订单预测服务实现类
 * 采用时间序列平滑法结合状态转移概率模型进行预测
 */
@Service
public class OrderRunPredictionServiceImpl implements IOrderRunPredictionService {

    @Autowired
    private AllRunMapper allRunMapper;

    @Autowired
    private OrderRunMapper orderRunMapper;

    /**
     * 订单状态映射表
     * 与AllRun和OrderRun实体类中的状态定义保持一致
     */
    private static final Map<Integer, String> STATUS_MAP = new HashMap<Integer, String>() {{
        put(0, "待接单");
        put(1, "已接单");
        put(2, "已完成");
        put(3, "已结算");
        put(4, "已取消");
        put(5, "已过期");
    }};

    @Override
    public List<OrderRunPredictionDTO> predictOrderStatusDistribution(Integer days) {
        // 默认预测7天
        if (days == null || days <= 0) {
            days = 7;
        }

        // 获取过去30天的历史数据作为预测基础
        LocalDateTime thirtyDaysAgo = LocalDateTime.now().minusDays(30);
        List<AllRun> historicalAllRuns = getAllRunHistory(thirtyDaysAgo);
        List<OrderRun> historicalOrderRuns = getOrderRunHistory(thirtyDaysAgo);

        // 数据预处理：按日期和状态聚合历史数据
        Map<LocalDate, Map<Integer, Integer>> dailyStatusCount = aggregateHistoricalData(historicalAllRuns, historicalOrderRuns);

        // 计算各状态的日均订单量和增长率
        Map<Integer, Double> statusDailyAvg = calculateDailyAverages(dailyStatusCount);
        Map<Integer, Double> statusGrowthRates = calculateGrowthRates(dailyStatusCount);

        // 生成未来7天的预测数据
        List<OrderRunPredictionDTO> predictions = generatePredictions(days, statusDailyAvg, statusGrowthRates);

        return predictions;
    }

    /**
     * 获取指定日期之后的all_run历史数据
     */
    private List<AllRun> getAllRunHistory(LocalDateTime startTime) {
        QueryWrapper<AllRun> queryWrapper = new QueryWrapper<>();
        queryWrapper.ge("create_time", startTime);
        queryWrapper.select("create_time", "state");
        return allRunMapper.selectList(queryWrapper);
    }

    /**
     * 获取指定日期之后的order_run历史数据
     */
    private List<OrderRun> getOrderRunHistory(LocalDateTime startTime) {
        QueryWrapper<OrderRun> queryWrapper = new QueryWrapper<>();
        queryWrapper.ge("create_time", startTime);
        queryWrapper.select("create_time", "order_status");
        return orderRunMapper.selectList(queryWrapper);
    }

    /**
     * 聚合历史数据：按日期和状态统计订单数量
     */
    private Map<LocalDate, Map<Integer, Integer>> aggregateHistoricalData(List<AllRun> allRuns, List<OrderRun> orderRuns) {
        Map<LocalDate, Map<Integer, Integer>> dailyStatusCount = new TreeMap<>();

        // 处理all_run数据
        for (AllRun run : allRuns) {
            if (run.getCreateTime() == null || run.getState() == null) continue;
            LocalDate date = run.getCreateTime().toLocalDate();
            int status = run.getState();
            dailyStatusCount.computeIfAbsent(date, k -> new HashMap<>())
                    .merge(status, 1, Integer::sum);
        }

        // 处理order_run数据
        for (OrderRun order : orderRuns) {
            if (order.getCreateTime() == null || order.getOrderStatus() == null) continue;
            LocalDate date = order.getCreateTime().toLocalDate();
            int status = order.getOrderStatus();
            dailyStatusCount.computeIfAbsent(date, k -> new HashMap<>())
                    .merge(status, 1, Integer::sum);
        }

        return dailyStatusCount;
    }

    /**
     * 计算各状态的日均订单量
     */
    private Map<Integer, Double> calculateDailyAverages(Map<LocalDate, Map<Integer, Integer>> dailyStatusCount) {
        Map<Integer, Integer> statusTotal = new HashMap<>();
        int dayCount = dailyStatusCount.size();

        for (Map<Integer, Integer> statusCount : dailyStatusCount.values()) {
            for (Map.Entry<Integer, Integer> entry : statusCount.entrySet()) {
                statusTotal.merge(entry.getKey(), entry.getValue(), Integer::sum);
            }
        }

        // 计算日均订单量
        Map<Integer, Double> statusDailyAvg = new HashMap<>();
        for (Map.Entry<Integer, Integer> entry : statusTotal.entrySet()) {
            statusDailyAvg.put(entry.getKey(), (double) entry.getValue() / dayCount);
        }

        return statusDailyAvg;
    }

    /**
     * 计算各状态的增长率
     */
    private Map<Integer, Double> calculateGrowthRates(Map<LocalDate, Map<Integer, Integer>> dailyStatusCount) {
        Map<Integer, Double> growthRates = new HashMap<>();
        if (dailyStatusCount.size() < 2) {
            // 数据不足时，使用默认增长率0.05
            STATUS_MAP.keySet().forEach(status -> growthRates.put(status, 0.05));
            return growthRates;
        }

        // 按日期排序
        List<LocalDate> sortedDates = new ArrayList<>(dailyStatusCount.keySet());
        Collections.sort(sortedDates);

        // 计算最近两周的增长率
        LocalDate twoWeeksAgo = LocalDate.now().minusWeeks(2);
        List<LocalDate> recentDates = sortedDates.stream()
                .filter(date -> date.isAfter(twoWeeksAgo))
                .collect(Collectors.toList());

        if (recentDates.size() < 2) {
            recentDates = sortedDates;
        }

        // 计算各状态的增长率
        for (Integer status : STATUS_MAP.keySet()) {
            Double firstCount = getStatusCount(dailyStatusCount, recentDates.get(0), status);
            Double lastCount = getStatusCount(dailyStatusCount, recentDates.get(recentDates.size() - 1), status);

            // 避免除以零
            if (firstCount <= 0) {
                growthRates.put(status, 0.1); // 默认增长率10%
            } else {
                double growthRate = (lastCount / firstCount - 1) / recentDates.size();
                growthRates.put(status, Math.max(growthRate, 0.01)); // 最低增长率1%
            }
        }

        return growthRates;
    }

    /**
     * 生成未来days天的预测数据
     */
    private List<OrderRunPredictionDTO> generatePredictions(Integer days, Map<Integer, Double> statusDailyAvg, Map<Integer, Double> statusGrowthRates) {
        List<OrderRunPredictionDTO> predictions = new ArrayList<>();
        LocalDate today = LocalDate.now();

        for (int i = 1; i <= days; i++) {
            LocalDate predictDate = today.plusDays(i);

            for (Map.Entry<Integer, String> statusEntry : STATUS_MAP.entrySet()) {
                Integer status = statusEntry.getKey();
                String statusName = statusEntry.getValue();

                // 获取该状态的日均订单量和增长率
                double avg = statusDailyAvg.getOrDefault(status, 5.0); // 默认日均5单
                double growthRate = statusGrowthRates.getOrDefault(status, 0.05); // 默认增长率5%

                // 指数平滑预测：考虑增长率和时间衰减
                double predictedValue = avg * Math.exp(growthRate * i);
                int predictedQuantity = (int) Math.round(predictedValue);

                // 计算置信区间（±20%）
                int lowerBound = (int) Math.round(predictedQuantity * 0.8);
                int upperBound = (int) Math.round(predictedQuantity * 1.2);

                // 创建预测DTO对象
                OrderRunPredictionDTO prediction = new OrderRunPredictionDTO();
                prediction.setPredictDate(predictDate);
                prediction.setStatus(status);
                prediction.setStatusName(statusName);
                prediction.setPredictQuantity(predictedQuantity);
                prediction.setLowerBound(lowerBound);
                prediction.setUpperBound(upperBound);

                predictions.add(prediction);
            }
        }

        return predictions;
    }

    /**
     * 获取指定日期指定状态的订单数量
     */
    private Double getStatusCount(Map<LocalDate, Map<Integer, Integer>> dailyStatusCount, LocalDate date, Integer status) {
        Map<Integer, Integer> statusCount = dailyStatusCount.get(date);
        if (statusCount == null) return 0.0;
        return (double) statusCount.getOrDefault(status, 0);
    }
}